////////////////////////////////////////////////////////////////////////////////
//
//  MICROSOFT CORPORATION
//  Copyright (c) Microsoft Corporation.
//  All Rights Reserved.
//
//  NOTICE: Microsoft Confidential. Intended for Internal Use Only.
//
////////////////////////////////////////////////////////////////////////////////

package com.msn.beet.core
{

import com.adobe.errors.IllegalStateError;
import com.msn.beet.errors.ApplicationError;
import com.msn.beet.events.ApplicationEvent;

import flash.display.DisplayObjectContainer;
import flash.display.Stage;
import flash.events.EventDispatcher;
import flash.system.ApplicationDomain;
import flash.system.Capabilities;
import flash.system.Security;
import flash.system.SecurityDomain;
import flash.utils.Dictionary;

/**
 * The Application static class serves as the application framework and
 * encapsulates common application-scoped functionality.
 *
 * <p>It provides global services to all objects used in the
 * application.</p>
 */
public final class Application extends StaticClass
{
    include "../core/Version.as";

    public static const PLAYER_TYPE_STANDALONE:String = "StandAlone";
    public static const PLAYER_TYPE_EXTERNAL:String = "External";
    public static const PLAYER_TYPE_ACTIVEX:String = "ActiveX";
    public static const PLAYER_TYPE_PLUGIN:String = "PlugIn";

    /**
     * Internal config settings
     */
    private static var externalConfig:XML = new XML();
    private static var applicationStartTime:Date = new Date();
    private static var applicationRoot:DisplayObjectContainer;
    private static var eventDispatcher:EventDispatcher;
    private static var hasFullScreen:Boolean = true;

    /**
     * Data containers for properties
     */
    private static var _config:XML;
    private static var _feeds:Dictionary = new Dictionary();
    private static var _assets:Dictionary = new Dictionary();
    private static var _projectSettings:Dictionary = new Dictionary();
    private static var _clientPlayerSupported:Boolean = true;
    private static var _logLevel:String = "debug";
    private static var _baseSiteDomain:String = "";
    private static var _baseSwfPath:String = "";
    private static var _baseContentPath:String = "";
    private static var _baseImagePath:String = "";
    private static var _externalConfigPath:String = "";

    /**
     * Initializes the Application.
     * @param target an instance of the document class
     */
    internal static function initialize(target:DisplayObjectContainer):void
    {
        applicationRoot = target;
        eventDispatcher = new EventDispatcher();

        // For compatibility with version 9,0,16,0 or earlier
        // Must be version 9,0,28,0 or later for Fullscreen support
        // this || works because this is AS3 therefore player is at least 9....
        hasFullScreen = Application.getPlayerMajorRevision() > 9 ||
                        Application.getPlayerMinorRevision() >= 28;

        if (!hasFullScreen)
        {
            return;
        }

        try
        {
            // import classes only available to version 9,0,28,0 and later
            import flash.events.FullScreenEvent;
            import flash.display.StageDisplayState;

            // add the full screen listener to the stage
            Application.stage.addEventListener(
                FullScreenEvent.FULL_SCREEN,
                function():void
                {
                    switch (Application.stage.displayState)
                    {
                        case StageDisplayState.FULL_SCREEN:
                        {
                            eventDispatcher.dispatchEvent(new ApplicationEvent(
                                    ApplicationEvent.FULLSCREEN_ENABLED));
                            break;
                        }
                        case StageDisplayState.NORMAL:
                        {
                            eventDispatcher.dispatchEvent(new ApplicationEvent(
                                    ApplicationEvent.FULLSCREEN_DISABLED));
                            break;
                        }
                    }
                }); // end closure
        }
        catch(e:Error)
        {
            // if error occurs, we can assume user does not have fullscreen
            // capabilities to be on the safe side.
            hasFullScreen = false;
        }

    }

    internal static function config(configuration:XML):void
    {
        // assign the configuration file instance
        _config = configuration;

        // initialize the application according to the Configuration XML
        if (_config && _config.appSettings.hasComplexContent())
        {
            try
            {
                externalConfig =
                    new XML(_config.externalSettings.toXMLString());

                // set log level
                if (_config.appSettings.debug.outputLevel.@value != "")
                {
                    _logLevel = String(
                        _config.appSettings.debug.outputLevel.@value
                        ).toUpperCase();
                }
                else
                {
                    throw new ApplicationError(
                        "Missing debug level in config xml",
                        ApplicationError.MISSING_DEBUG_LEVEL);
                }

                // check minimum player version
                var minPlayerVersion:String =
                    _config.appSettings.minimumPlayerVersion.@value;

                if (minPlayerVersion != "")
                {
                    _clientPlayerSupported =
                        Application.getPlayerMinorRevision() >=
                        uint(minPlayerVersion.split(",")[2])
                }

                // set base paths
                _baseSiteDomain = getFlashVarPathFromConfig(
                                    XML(_config.appSettings.baseSiteDomain));

                _baseSwfPath = getFlashVarPathFromConfig(
                                    XML(_config.appSettings.baseSwfPath));

                _baseContentPath = getFlashVarPathFromConfig(
                                    XML(_config.appSettings.baseContentPath));

                _baseImagePath = getFlashVarPathFromConfig(
                                    XML(_config.appSettings.baseImagePath));

                var securitySettings:XMLList = _config.appSettings.security;
                applySecuritySettings(securitySettings);
            }
            catch(e:Error)
            {
                throw new ApplicationError("Application has encountered " +
                        "a malformed Configuration setting. Aborting " +
                        "Application initialization. " + e,
                        ApplicationError.MALFORMED_CONFIGURATION_SETTING);
            }
        }
        else
        {
            //WARNING: Configuration file not found
        }
    }

    internal static function get externalConfigPath():String
    {
        if (_config)
        {
            return getFlashVarPathFromConfig(
                XML(_config.appSettings.externalConfigPath))
        }
        return "";
    }

    /**
     * Externally-loaded config file
     */
    internal static function get externalSettings():XML
    {
        return externalConfig;
    }

    /**
     * Sets start time when ApplicationRuntime has initialized.
     * @param time
     */
    internal static function setStartTime(time:Date):void
    {
        applicationStartTime = time;
    }

    internal static function setDictionaries(feeds:Dictionary,
                                             assets:Dictionary,
                                             projectSettings:Dictionary
                                             ):void
    {
        _feeds = feeds;
        _assets = assets;
        _projectSettings = projectSettings;
    }

    internal static function applySecuritySettings(securitySettings:XMLList
                                                   ):void
    {
        if (securitySettings)
        {
            // load policy files if given
            // [vaclav] DO NOT remove children() method - it is
            // required for proper enumeration
            for each (var policyItem:XML in securitySettings
                                            .crossDomainPolicies
                                            .children())
            {
                if (policyItem.@value != "")
                {
                    Security.loadPolicyFile(policyItem.@value);
                }
            }

            // set up allowed domains
            // [vaclav] same as above
            for each (var domainItem:XML in securitySettings
                                            .allowedDomains
                                            .children())
            {
                if (domainItem.@value != "")
                {
                    Security.allowDomain(domainItem.@value);
                }
            }
        }
    }

    /**
     * Adds an event listener to the top-level Application. Refer to
     * EventDispatcher.addEventListener docs for detailed information.
     * @param type:String
     * @param listener:Function
     * @param useCapture:Boolean
     * @param priority:int
     * @param useWeakReference:Boolean
     */
    public static function addEventListener(type:String, listener:Function,
                                            useCapture:Boolean = false,
                                            priority:int = 0,
                                            useWeakReference:Boolean = true
                                            ):void
    {
        eventDispatcher.addEventListener(type, listener, useCapture, priority,
                                          useWeakReference);
    }


    /**
     * Checks for a specific event registered with Applications
     * eventDispatcher.
     * @param type
     * @return
     */
    public static function hasEventListener(type:String):Boolean
    {
        return eventDispatcher.hasEventListener(type)
    }

    /**
     * Removes an event listener from the top-level Application. Refer to
     * EventDispatcher.removeEventListener docs for detailed information.
     * @param type:String
     * @param listener:Function
     * @param useCapture:Boolean
     */
    public static function removeEventListener(type:String, listener:Function,
                                               useCapture:Boolean = false):void
    {
        eventDispatcher.removeEventListener(type, listener, useCapture);
    }

    /**
     * Gets a specific Flashvar that is required by default
     * @param name name of the Flashvar
     * @param required:Boolean if true, an Application Error is thrown if
     * the flashvar is missing (default value is true)
     * @return String null is returned if the Flashvar was not found and was
     * not required
     */
    public static function getFlashVar(name:String,
                                       required:Boolean = true):String
    {
        var value:String = Application.parameters[name];

        if (required && value == null)
        {
            throw new ApplicationError(
                "Missing required Flashvar: " + name,
                ApplicationError.MISSING_REQUIRED_FLASHVAR);
        }

        return value;
    }

    /**
     * Checks if the Flash Player is currently in full screen mode
     * @return Boolean
     */
    public static function inFullscreen():Boolean
    {
        if (Application.fullscreenAvailable)
        {
            return Application.stage.displayState ==
                   flash.display.StageDisplayState.FULL_SCREEN;
        }
        return false;
    }

    /**
     * Sets the frame rate of the application to the specified value
     * @param value:uint
     */
    public static function setFrameRate(value:uint):void
    {
        stage.frameRate = value;
    }

    /**
     * The Stage object for this application
     * @return Stage
     */
    public static function get stage():Stage
    {
        if (!applicationRoot)
        {
            throw new ApplicationError(
                "Initialize method must be called before accessing the stage",
                ApplicationError.MISSING_CALL_TO_INITIALIZE);
        }
        return applicationRoot.stage;
    }

    /**
     * List of application parameters (aka flashVars)
     * @return Object
     */
    public static function get parameters():Object
    {
        return applicationRoot.loaderInfo.parameters;
    }

    /**
     * The nominal frame rate is the rate at which the application is
     * currently running.
     * This may differ from the frame rate set with the [SWF] attribute or
     * using setFrameRate()
     * @return uint
     */
    public static function get nominalFrameRate():uint
    {
        return applicationRoot.loaderInfo.frameRate;
    }

    /**
     * Gets the url of the application
     * @return String
     */
    public static function get url():String
    {
        return applicationRoot.loaderInfo.url;
    }

    /**
     * Application domain of this application
     * @return ApplicationDomain
     */
    public static function get applicationDomain():ApplicationDomain
    {
        return applicationRoot.loaderInfo.applicationDomain;
    }

    /**
     * Site Domain
     * @return String
     */
    public static function get baseSiteDomain():String
    {
        return _baseSiteDomain;
    }

    /**
     * Base URL for static server Swf assets
     * @return String
     */
    public static function get baseSwfPath():String
    {
       return _baseSwfPath;
    }

    /**
     * Base URL for static server contents
     * @return String
     */
    public static function get baseContentPath():String
    {
        return _baseContentPath;
    }

    /**
     * Base URL for static server image assets
     * @return String
     */
    public static function get baseImagePath():String
    {
        return _baseImagePath;
    }

    /**
     * The current security domain of the application
     * @return SecurityDomain
     */
    public static function get currentDomain():SecurityDomain
    {
        return SecurityDomain.currentDomain;
    }

    /**
     * This property is true if Flash Player supports full screen mode
     * @return Boolean
     */
    public static function get fullscreenAvailable():Boolean
    {
        return hasFullScreen;
    }

    /**
     * Tells if the client's Flash Player version is supported by this
     * Application, according to the version specified in the Configuration
     * file
     * @return Boolean
     */
    public static function get clientPlayerSupported():Boolean
    {
        return _clientPlayerSupported;
    }

    /**
     * Flash player type (Flash IDE, standalone, ActiveX, Plugin)
     * @return String
     */
    public static function get playerType():String
    {
        return Capabilities.playerType;
    }

    /**
     * String value of the application logLevel.
     * @see LogLevel
     * @return
     */
    public static function get logLevel():String
    {
        return _logLevel;
    }

    /**
     * Returns a date object corresponding to the time the main application
     * was started.
     * @return
     */
    public static function get startTime():Date
    {
        return applicationStartTime;
    }

    /**
     * Number of milliseconds since the Main Application has started.
     * @return
     */
    public static function get upTime():uint
    {
        return new Date().time - applicationStartTime.time;
    }

    public static function get feeds():Dictionary
    {
        return _feeds
    }

    public static function get assets():Dictionary
    {
        return _assets
    }

    public static function get projectSettings():Dictionary
    {
        return _projectSettings
    }

    /**
     * Gets the current Flash Player revision number
     */
    public static function getPlayerMinorRevision():uint
    {
        return uint(Capabilities.version.split(",")[2]);
    }

    public static function getPlayerMajorRevision():uint
    {
        return uint(Capabilities.version.split(",")[0]);
    }

    private static function getFlashVarPathFromConfig(node:XML):String
    {
        var path:String = "";
        if (node.@flashVar != "")
        {
            // check for the flashvar value
            path = Application.parameters[node.@flashVar];

            if (path == null)
            {
                // flashvar not provided - check for a default in the config
                path = node.@defaultValue;
                if (path == null)
                {
                    throw new IllegalStateError(
                        "Error "
                        + ApplicationError.MISSING_REQUIRED_FLASHVAR
                        + ": Missing required Flashvar: "
                        + node.@flashVar);
                }
            }
        }
        else
        {
            // Flashvar name not given in config, check the default path
            path = node.@defaultValue;
        }

        return path;
    }

}

}
